package strains;

import genes.AmbiguityCodes;
import genes.Codon;
import genes.GeneSequence;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import printers.PlotPrinter;
import printers.QuartetFilePrinter;


import dataStructures.Triple;

public class Quartet {
	
	public Strain strain1;
	public Strain strain2;
	public Strain strain3;
	public Strain strain4;
	private String QuartetID;
	private Triple triple;
	int[] i;
	private QuartetFilePrinter qfPrinter;
	private int divergence;
	private int originalDivergence;
	private PlotPrinter PlotPrinter;
	
	public Quartet(QuartetFilePrinter Qfprinter,PlotPrinter pp,StrainPicker sp) throws IOException{
	//	StrainPicker sp = new StrainPicker();
		qfPrinter = Qfprinter;
		PlotPrinter=pp;
		String[] temp =sp.PickStrains();
		i= sp.getPicked();
		this.strain1=new Strain(temp[0]);
		this.strain2=new Strain(temp[1]);
		this.strain3=new Strain(temp[2]);
		this.strain4=new Strain(temp[3]);
	//	System.out.println(this.strain1.getSequence());
	//	System.out.println(this.strain2.getSequence());
	//	System.out.println(this.strain3.getSequence());
	//	System.out.println(this.strain4.getSequence());
		
		this.printLinetoQuartetFile("Strains "+i[0]+","+i[1]+","+i[2]+","+i[3]);
		this.printLinetoQuartetFile("Original Sequence");
		this.printQuartetToFile();
		this.originalDivergence=this.setDivergenceAge();
		this.printLinetoQuartetFile("Original Divergence Age="+this.divergence);// divergence!!!
		removeNonSilentMutations();
		this.printLinetoQuartetFile("NonSilent Mutations Removed");
		this.printQuartetToFile();
		removeAmbiguityCodes();
		this.divergence=this.setDivergenceAge();
		this.printLinetoQuartetFile("Ambiguities Removed");
		this.printQuartetToFile();
		removeNonInformativeSites();
		this.printLinetoQuartetFile("InformativeSites");
		this.printQuartetToFile();
		mapSitesToTree();
	
		if(checkValidTriple(this.triple)){
		this.printLinetoQuartetFile(this.triple.displayTriple());
		this.printLinetoQuartetFile("Original Divergence Age="+this.originalDivergence);// divergence!!!
		this.printLinetoQuartetFile("Divergence Age="+this.divergence);// divergence!!!
		this.printLinetoQuartetFile("Clonality Measure= "+this.triple.getClonalityMeasure());	
		this.printLinetoQuartetFile("Expected Clonality= "+this.triple.getExcpetedClonality());	
		this.printLinetoQuartetFile("Max Clonailty= "+this.triple.getMaxClonality());	
		this.printLinetoQuartetFile("Normed Clonailty= "+this.triple.getNormedClonality());	
		this.printLinetoQuartetFile("Probability= "+this.triple.getProb());	
		this.printPlot();// this is done here because we need to only get relivent ones
		//System.out.println(this.triple.getClonalityMeasure());
		//System.out.println(this.triple.getExcpetedClonality());
		//System.out.println(this.triple.getNormedClonality());
		//System.out.println(this.triple.getMaxClonality());
		}
	}
	
	public int setDivergenceAge(){
		int[] hds= new int[6];
		hds[0]= this.strain1.hammingDist(this.strain2.getSequence());
		hds[1]= this.strain1.hammingDist(this.strain3.getSequence());
		hds[2]= this.strain1.hammingDist(this.strain4.getSequence());
		hds[3]= this.strain2.hammingDist(this.strain3.getSequence());
		hds[4]= this.strain2.hammingDist(this.strain4.getSequence());
		hds[5]= this.strain3.hammingDist(this.strain4.getSequence());
		Arrays.sort(hds);
		System.out.println(hds[0]);
		System.out.println(hds[1]);
		System.out.println(hds[2]);
		System.out.println(hds[3]);
		System.out.println(hds[4]);
		System.out.println(hds[5]);
	//	this.divergence= hds[0];
		return hds[0];
		
	}

	private boolean checkValidTriple(Triple trip) {
		return(!(triple.sumTriple()==0));
		
	}


	public void removeNonSilentMutations() throws IOException{
		List<Codon> s1= this.strain1.getAminoAcidSequence();
		List<Codon> s2= this.strain2.getAminoAcidSequence();
		List<Codon> s3= this.strain3.getAminoAcidSequence();
		List<Codon> s4= this.strain4.getAminoAcidSequence();
	
		System.out.println(this.strain1.getGeneSequence().printAminoAcids());
		System.out.println(this.strain2.getGeneSequence().printAminoAcids());
		System.out.println(this.strain3.getGeneSequence().printAminoAcids());
		System.out.println(this.strain4.getGeneSequence().printAminoAcids());
		
		Iterator<Codon> iter1= s1.listIterator();
		Iterator<Codon> iter2= s2.listIterator();
		Iterator<Codon> iter3= s3.listIterator();
		Iterator<Codon> iter4= s4.listIterator();
		HashSet<Integer> CodonPositions = new HashSet<Integer>();
		
		int pos=1;
		
		while(iter1.hasNext()){
			Codon x1= iter1.next();
		//	System.out.println(x1.getCodon());
			Codon x2= iter2.next();
		//	System.out.println(x2.getCodon());
			Codon x3= iter3.next();
		//	System.out.println(x3.getCodon());
			Codon x4= iter4.next();
		//	System.out.println(x4.getCodon());
			if((x1.equals((x2))&&(x2.equals(x3))&&(x3.equals(x4)))){
		//	System.out.println(x1.getCodon()+ x2.getCodon()+x3.getCodon()+x4.getCodon());
			}else{
			//	System.out.println(pos);
				CodonPositions.addAll(getPosOfMutations(pos));
			}
									
				
			
			pos++;
			
		}
//		System.out.println(printListInt(CodonPositions));
		Iterator<Integer> cPosIter= CodonPositions.iterator();

			
		char[] ms1= this.strain1.getSequence().toCharArray();
		char[] ms2= this.strain2.getSequence().toCharArray();
		char[] ms3= this.strain3.getSequence().toCharArray();
		char[] ms4= this.strain4.getSequence().toCharArray();
		cPosIter.next();
		while( cPosIter.hasNext()){
			int remove = cPosIter.next();
		//	System.out.println(remove);
			ms1[remove]=' ';
			ms2[remove]=' ';
			ms3[remove]=' ';
			ms4[remove]=' ';
		
		}
		String l1= "";
		String l2= "";
		String l3= "";
		String l4= "";
	
		int count=0;
		while(count!=ms1.length){
			if(ms1[count]!=' ')
				l1=l1+ms1[count];
			if(ms2[count]!=' ')
				l2=l2+ms2[count];
			if(ms3[count]!=' ')
				l3=l3+ms3[count];
			if(ms4[count]!=' ')
				l4=l4+ms4[count];
			count++;
		}
		
		
		this.strain1.setSequence(String.valueOf(ms1));
		this.strain2.setSequence(String.valueOf(ms2));
		this.strain3.setSequence(String.valueOf(ms3));
		this.strain4.setSequence(String.valueOf(ms4));
		
		System.out.println(this.strain1.getSequence());
		System.out.println(this.strain2.getSequence());
		System.out.println(this.strain3.getSequence());
		System.out.println(this.strain4.getSequence());
	
	}
	
	


	private List<Integer> getPosOfMutations(int posy) {
		List<Integer> positions= new ArrayList<Integer>();
		int pos=posy*3;
	//	positions.add(0);
		positions.add(pos-1);
		positions.add(pos-2);
		positions.add(pos-3);
				return positions;
	}
	
	private void removeAmbiguityCodes() throws IOException{
	
		 String gs1 = strain1.getGeneSequence().geneSequence;
		 String gs2 = strain2.getGeneSequence().geneSequence;
		 String gs3 = strain3.getGeneSequence().geneSequence;
		 String gs4 = strain4.getGeneSequence().geneSequence;
	
		HashSet<Integer> ambigityPos= new HashSet<Integer>();
		AmbiguityCodes ac= new AmbiguityCodes();
		int posi=0;
		while(gs1.length()!=posi){
			if(ac.isAmbiguityCode(gs1.charAt(posi))){
				ambigityPos.add(posi);
			}
			if(ac.isAmbiguityCode(gs2.charAt(posi))){
				ambigityPos.add(posi);
			}
			if(ac.isAmbiguityCode(gs3.charAt(posi))){
				ambigityPos.add(posi);
			}
			if(ac.isAmbiguityCode(gs4.charAt(posi))){
				ambigityPos.add(posi);
			}
			posi++;
		}
		Iterator<Integer> acIter= ambigityPos.iterator();
		char[] gs1c= gs1.toCharArray();
		char[] gs2c= gs2.toCharArray();
		char[] gs3c= gs3.toCharArray();
		char[] gs4c= gs4.toCharArray();
		while(acIter.hasNext()){
			int remove =acIter.next();
			gs1c[remove]=' ';// or emtpy unicode amy
			gs2c[remove]=' ';// or emtpy unicode amy
			gs3c[remove]=' ';// or emtpy unicode amy
			gs4c[remove]=' ';// or emtpy unicode amy
		}
		this.strain1.setSequence(String.valueOf(gs1c));
		this.strain2.setSequence(String.valueOf(gs2c));
		this.strain3.setSequence(String.valueOf(gs3c));
		this.strain4.setSequence(String.valueOf(gs4c));
		
		System.out.println(this.strain1.getSequence());
		System.out.println(this.strain2.getSequence());
		System.out.println(this.strain3.getSequence());
		System.out.println(this.strain4.getSequence());
	
	}
	
	private void removeNonInformativeSites() throws IOException{
		Iterator<Integer> iter =this.getInformativeSites().iterator();
		char[] ts1=this.strain1.getSequence().toCharArray();
		char[] ts2=this.strain2.getSequence().toCharArray();
		char[] ts3=this.strain3.getSequence().toCharArray();
		char[] ts4=this.strain4.getSequence().toCharArray();
		while(iter.hasNext()){
			int remove = iter.next();
			ts1[remove]=' ';
			ts2[remove]=' ';
			ts3[remove]=' ';
			ts4[remove]=' ';
		}
	
	
		
		this.strain1.setSequence(String.valueOf(ts1));
		this.strain2.setSequence(String.valueOf(ts2));
		this.strain3.setSequence(String.valueOf(ts3));
		this.strain4.setSequence(String.valueOf(ts4));
		System.out.println("\nUninformative Sites removed");
		System.out.println(this.strain1.getSequence());
		System.out.println(this.strain2.getSequence());
		System.out.println(this.strain3.getSequence());
		System.out.println(this.strain4.getSequence());
	}
	
	private String printList(List<String> x){
		Iterator<String> iter= x.iterator();
		String r="";
		while(iter.hasNext()){
			r=r+iter.next();
		//	System.out.println(r);
		}
	//	System.out.println(r);
		return r;
	}
	
	private String printListInt(List<Integer> x){
		Iterator<Integer> iter= x.iterator();
		String r="";
		while(iter.hasNext()){
			r=r+iter.next();
		//	System.out.println(r);
		}
	//	System.out.println(r);
		return r;
	}
	
	private String printListInt(HashSet<Integer> x) {
		Iterator<Integer> iter= x.iterator();
		String r="";
		while(iter.hasNext()){
			r=r+","+iter.next();
		//	System.out.println(r);
		}
	//	System.out.println(r);
		return r;
		
	}
	
	public HashSet<Integer> getInformativeSites(){
		HashSet<Integer> UninformativeSites= new HashSet<Integer>();
		HashSet<Integer> informativeSites= new HashSet<Integer>();
		int pos=0;
		while(pos!=this.strain1.getSequence().length()){
		String currentSite=this.getSite(pos);
		if(!isInformative(currentSite)){
			UninformativeSites.add(pos);
		}
		if(isInformative(currentSite)){
			informativeSites.add(pos);
		}
		pos++;
		}
	//	Iterator<Integer> iter=informativeSites.iterator();
	//	while(iter.hasNext()){
	//		System.out.println(iter.next());
	//	}
		return UninformativeSites;
		
	}
	

	
	// test this function
	public boolean isInformative(String site){
		return (isTreeOne(site)||isTreeTwo(site)||isTreeThree(site));
	}
	
	private String getSite(int pos){
		return (String.valueOf(this.strain1.getSequence().charAt(pos))+
		String.valueOf(this.strain2.getSequence().charAt(pos))+
		String.valueOf(this.strain3.getSequence().charAt(pos))+
		String.valueOf(this.strain4.getSequence().charAt(pos)));
	}
	
	public boolean isTreeOne(String site){
		char a= site.charAt(0);
		char b= site.charAt(1);
		char c=site.charAt(2);
		char d= site.charAt(3);
		if(((a==b)&&(c==d))&&((a!=d)&&(b!=c))){
			return true;
		}else return false;
	}
	
	public boolean isTreeTwo(String site){
		char a= site.charAt(0);
		char b= site.charAt(1);
		char c=site.charAt(2);
		char d= site.charAt(3);
		if((a==c)&&(b==d)&& !(a==b)&& !(c==d)&&!(a==d)&&!(b==c)){
			return true;
		}else return false;
		
	}
	
	public boolean isTreeThree(String site){
		char a= site.charAt(0);
		char b= site.charAt(1);
		char c=site.charAt(2);
		char d= site.charAt(3);
		if((a==d)&&(b==c)&& !(a==b)&&!(a==c)&&!(b==d)&&!(c==d)){
			return true;
		}
		else return false;
	}
	
	private Triple mapSitesToTree(){
	
		int pos=0;
		int tree1=0;
		int tree2=0;
		int tree3=0;
		while(pos!=this.strain1.getSequence().length()){
			String current =this.getSite(pos);
		//	System.out.println(current);
			if(this.isTreeOne(current)){
				tree1++;
			}
			if(this.isTreeThree(current)){
				tree2++;
			}
			if(this.isTreeTwo(current)){
				tree3++;
			}
			pos++;
		}
		Triple res= new Triple(tree1,tree2,tree3);
		this.triple=res;
		System.out.println(this.triple.displayTriple());
		return res;
	}
	


	private void printQuartetToFile() throws IOException{
		this.qfPrinter.printToFile(this.strain1.getSequence());
		this.qfPrinter.printToFile(this.strain2.getSequence());
		this.qfPrinter.printToFile(this.strain3.getSequence());
		this.qfPrinter.printToFile(this.strain4.getSequence());
	}
	
	private void printLinetoQuartetFile(String line) throws IOException{
		this.qfPrinter.printToFile(line);
	}
	//prints
	//triple,divergence, CM,maxCM, expectedCM, NormedCM, and prob
	private void printPlot() throws IOException{
		String line=this.triple.displayTriple()+"	"+this.originalDivergence+"	"+this.divergence+"	"+this.triple.getClonalityMeasure()+
		"	"+this.triple.getMaxClonality()+"	"+this.triple.getExcpetedClonality()+"	"+
		this.triple.getNormedClonality()+"	"+this.triple.getProb();
		this.PlotPrinter.printToFile(line);
	}
}
